/* gnu.classpath.tools.doclets.AbstractDoclet
Copyright (C) 2004 Free Software Foundation, Inc.
This file is part of GNU Classpath.
GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
package gnu.classpath.tools.doclets;
import com.sun.javadoc.ClassDoc;
import com.sun.javadoc.ConstructorDoc;
import com.sun.javadoc.Doc;
import com.sun.javadoc.Doclet;
import com.sun.javadoc.ExecutableMemberDoc;
import com.sun.javadoc.FieldDoc;
import com.sun.javadoc.MethodDoc;
import com.sun.javadoc.PackageDoc;
import com.sun.javadoc.Parameter;
import com.sun.javadoc.RootDoc;
import com.sun.javadoc.Tag;
import com.sun.javadoc.Type;
import com.sun.tools.doclets.Taglet;
import gnu.classpath.tools.taglets.GnuExtendedTaglet;
import gnu.classpath.tools.taglets.AuthorTaglet;
import gnu.classpath.tools.taglets.CodeTaglet;
import gnu.classpath.tools.taglets.DeprecatedTaglet;
import gnu.classpath.tools.taglets.GenericTaglet;
import gnu.classpath.tools.taglets.SinceTaglet;
import gnu.classpath.tools.taglets.ValueTaglet;
import gnu.classpath.tools.taglets.VersionTaglet;
import gnu.classpath.tools.taglets.TagletContext;
import gnu.classpath.tools.IOToolkit;
import gnu.classpath.tools.FileSystemClassLoader;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.InvocationTargetException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import java.util.SortedSet;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.TreeSet;
/**
* An abstract Doclet implementation with helpers for common tasks
* performed by Doclets.
*/
public abstract class AbstractDoclet
{
/**
* Mapping from tag type to Taglet for user Taglets specified on
* the command line.
*/
protected Map tagletMap = new LinkedHashMap();
/**
* Stores the package groups specified in the user
* options. Contains objects of type PackageGroup.
*/
private List packageGroups = new LinkedList();
/**
* The current classpath for loading taglet classes.
*/
private String tagletPath;
/**
* Keeps track of the tags mentioned by the user during option
* processiong so that an error can be emitted if a tag is
* mentioned more than once.
*/
private List mentionedTags = new LinkedList();
public static int optionLength(String option) {
return instance.getOptionLength(option);
}
public static boolean validOptions(String[][] options) {
return true;
}
private static AbstractDoclet instance;
protected static void setInstance(AbstractDoclet instance)
{
AbstractDoclet.instance = instance;
}
protected abstract void run()
throws DocletConfigurationException, IOException;
public static boolean start(RootDoc rootDoc)
{
try {
instance.startInstance(rootDoc);
return true;
}
catch (DocletConfigurationException e) {
instance.printError(e.getMessage());
return false;
}
catch (Exception e) {
e.printStackTrace();
return false;
}
}
protected RootDoc getRootDoc()
{
return this.rootDoc;
}
private RootDoc rootDoc;
protected abstract InlineTagRenderer getInlineTagRenderer();
private void startInstance(RootDoc rootDoc)
throws DocletConfigurationException, IOException
{
this.rootDoc = rootDoc;
// Set the default Taglet order
registerTaglet(new VersionTaglet());
registerTaglet(new AuthorTaglet());
registerTaglet(new SinceTaglet(getInlineTagRenderer()));
registerTaglet(new StandardTaglet("serial"));
registerTaglet(new StandardTaglet("deprecated"));
registerTaglet(new StandardTaglet("see"));
registerTaglet(new StandardTaglet("param"));
registerTaglet(new StandardTaglet("return"));
registerTaglet(new ValueTaglet());
registerTaglet(new CodeTaglet());
// Process command line options
for (int i=0, ilim=rootDoc.options().length; i<ilim; ++i) {
String[] optionArr = rootDoc.options()[i];
String _optionTag = optionArr[0];
DocletOption option = (DocletOption)nameToOptionMap.get(_optionTag.toLowerCase());
if (null != option) {
option.set(optionArr);
}
}
// Enable/disable standard taglets based on user input
AuthorTaglet.setTagletEnabled(optionAuthor.getValue());
VersionTaglet.setTagletEnabled(optionVersion.getValue());
SinceTaglet.setTagletEnabled(!optionNoSince.getValue());
DeprecatedTaglet.setTagletEnabled(!optionNoDeprecated.getValue());
if (!getTargetDirectory().exists()) {
if (!getTargetDirectory().mkdirs()) {
throw new DocletConfigurationException("Cannot create target directory "
+ getTargetDirectory());
}
}
run();
}
public File getTargetDirectory()
{
return optionTargetDirectory.getValue();
}
private DocletOptionFile optionTargetDirectory =
new DocletOptionFile("-d",
new File(System.getProperty("user.dir")));
private DocletOptionFlag optionNoEmailWarn =
new DocletOptionFlag("-noemailwarn");
private DocletOptionFlag optionAuthor =
new DocletOptionFlag("-author");
private DocletOptionFlag optionVersion =
new DocletOptionFlag("-version");
private DocletOptionFlag optionNoSince =
new DocletOptionFlag("-nosince");
private DocletOptionFlag optionNoDeprecated =
new DocletOptionFlag("-nodeprecated");
private DocletOptionGroup optionGroup =
new DocletOptionGroup("-group");
private DocletOptionPackageWildcard optionNoQualifier =
new DocletOptionPackageWildcard("-noqualifier", true);
private DocletOptionFlag optionDocFilesSubDirs =
new DocletOptionFlag("-docfilessubdirs");
private DocletOptionColonSeparated optionExcludeDocFilesSubDir =
new DocletOptionColonSeparated("-excludedocfilessubdir");
private DocletOptionTagletPath optionTagletPath =
new DocletOptionTagletPath("-tagletpath");
private DocletOptionTag optionTaglet =
new DocletOptionTag("-taglet");
private DocletOptionTag optionTag =
new DocletOptionTag("-tag");
private class DocletOptionTaglet
extends DocletOption
{
DocletOptionTaglet(String optionName)
{
super(optionName);
}
public int getLength()
{
return 2;
}
public boolean set(String[] optionArr)
{
boolean tagletLoaded = false;
String useTagletPath = AbstractDoclet.this.tagletPath;
if (null == useTagletPath) {
useTagletPath = System.getProperty("java.class.path");
}
try {
Class tagletClass;
try {
tagletClass
= new FileSystemClassLoader(useTagletPath).loadClass(optionArr[1]);
}
catch (ClassNotFoundException e) {
// If not found on specified tagletpath, try default classloader
tagletClass
= Class.forName(optionArr[1]);
}
Method registerTagletMethod
= tagletClass.getDeclaredMethod("register", new Class[] { java.util.Map.class });
if (!registerTagletMethod.getReturnType().equals(Void.TYPE)) {
printError("Taglet class '" + optionArr[1] + "' found, but register method doesn't return void.");
}
else if (registerTagletMethod.getExceptionTypes().length > 0) {
printError("Taglet class '" + optionArr[1] + "' found, but register method contains throws clause.");
}
else if ((registerTagletMethod.getModifiers() & (Modifier.STATIC | Modifier.PUBLIC | Modifier.ABSTRACT)) != (Modifier.STATIC | Modifier.PUBLIC)) {
printError("Taglet class '" + optionArr[1] + "' found, but register method isn't public static, or is abstract..");
}
else {
Map tempMap = new HashMap();
registerTagletMethod.invoke(null, new Object[] { tempMap });
tagletLoaded = true;
String name = (String)tempMap.keySet().iterator().next();
Taglet taglet = (Taglet)tempMap.get(name);
tagletMap.put(name, taglet);
mentionedTags.add(taglet);
}
}
catch (NoSuchMethodException e) {
printError("Taglet class '" + optionArr[1] + "' found, but doesn't contain the register method.");
}
catch (SecurityException e) {
printError("Taglet class '" + optionArr[1] + "' cannot be loaded: " + e.getMessage());
}
catch (InvocationTargetException e) {
printError("Taglet class '" + optionArr[1] + "' found, but register method throws exception: " + e.toString());
}
catch (IllegalAccessException e) {
printError("Taglet class '" + optionArr[1] + "' found, but there was a problem when accessing the register method: " + e.toString());
}
catch (IllegalArgumentException e) {
printError("Taglet class '" + optionArr[1] + "' found, but there was a problem when accessing the register method: " + e.toString());
}
catch (ClassNotFoundException e) {
printError("Taglet class '" + optionArr[1] + "' cannot be found.");
}
return tagletLoaded;
}
}
private class DocletOptionGroup
extends DocletOption
{
DocletOptionGroup(String optionName)
{
super(optionName);
}
public int getLength()
{
return 3;
}
public boolean set(String[] optionArr)
{
try {
PackageMatcher packageMatcher = new PackageMatcher();
StringTokenizer tokenizer = new StringTokenizer(optionArr[2], ":");
while (tokenizer.hasMoreTokens()) {
String packageWildcard = tokenizer.nextToken();
packageMatcher.addWildcard(packageWildcard);
}
SortedSet groupPackages = packageMatcher.filter(rootDoc.specifiedPackages());
packageGroups.add(new PackageGroup(optionArr[1], groupPackages));
return true;
}
catch (InvalidPackageWildcardException e) {
return false;
}
}
}
private class DocletOptionTagletPath
extends DocletOption
{
DocletOptionTagletPath(String optionName)
{
super(optionName);
}
public int getLength()
{
return 2;
}
public boolean set(String[] optionArr)
{
AbstractDoclet.this.tagletPath = optionArr[1];
return true;
}
}
private class DocletOptionTag
extends DocletOption
{
DocletOptionTag(String optionName)
{
super(optionName);
}
public int getLength()
{
return 2;
}
public boolean set(String[] optionArr)
{
String tagSpec = optionArr[1];
boolean validTagSpec = false;
int ndx1 = tagSpec.indexOf(':');
if (ndx1 < 0) {
Taglet taglet = (Taglet)tagletMap.get(tagSpec);
if (null == taglet) {
printError("There is no standard tag '" + tagSpec + "'.");
}
else {
if (mentionedTags.contains(taglet)) {
printError("Tag '" + tagSpec + "' has been added or moved before.");
}
else {
mentionedTags.add(taglet);
// re-append taglet
tagletMap.remove(tagSpec);
tagletMap.put(tagSpec, taglet);
}
}
}
else {
int ndx2 = tagSpec.indexOf(':', ndx1 + 1);
if (ndx2 > ndx1 && ndx2 < tagSpec.length() - 1) {
String tagName = tagSpec.substring(0, ndx1);
String tagHead = null;
if (tagSpec.charAt(ndx2 + 1) == '\"') {
if (tagSpec.charAt(tagSpec.length() - 1) == '\"') {
tagHead = tagSpec.substring(ndx2 + 2, tagSpec.length() - 1);
validTagSpec = true;
}
}
else {
tagHead = tagSpec.substring(ndx2 + 1);
validTagSpec = true;
}
boolean tagScopeOverview = false;
boolean tagScopePackages = false;
boolean tagScopeTypes = false;
boolean tagScopeConstructors = false;
boolean tagScopeMethods = false;
boolean tagScopeFields = false;
boolean tagDisabled = false;
tag_option_loop:
for (int n=ndx1+1; n<ndx2; ++n) {
switch (tagSpec.charAt(n)) {
case 'X':
tagDisabled = true;
break;
case 'a':
tagScopeOverview = true;
tagScopePackages = true;
tagScopeTypes = true;
tagScopeConstructors = true;
tagScopeMethods = true;
tagScopeFields = true;
break;
case 'o':
tagScopeOverview = true;
break;
case 'p':
tagScopePackages = true;
break;
case 't':
tagScopeTypes = true;
break;
case 'c':
tagScopeConstructors = true;
break;
case 'm':
tagScopeMethods = true;
break;
case 'f':
tagScopeFields = true;
break;
default:
validTagSpec = false;
break tag_option_loop;
}
}
if (validTagSpec) {
GenericTaglet taglet
= new GenericTaglet(tagName,
tagHead,
tagScopeOverview,
tagScopePackages,
tagScopeTypes,
tagScopeConstructors,
tagScopeMethods,
tagScopeFields);
taglet.setTagletEnabled(!tagDisabled);
taglet.register(tagletMap);
mentionedTags.add(taglet);
}
}
}
if (!validTagSpec) {
printError("Value for option -tag must be in format \"<tagname>:Xaoptcmf:<taghead>\".");
}
return validTagSpec;
}
}
private DocletOption[] commonOptions =
{
optionTargetDirectory,
optionAuthor,
optionVersion,
optionNoSince,
optionNoDeprecated,
optionGroup,
optionDocFilesSubDirs,
optionExcludeDocFilesSubDir,
optionTagletPath,
optionTaglet,
optionTag,
};
private void registerOptions()
{
if (!optionsRegistered) {
for (int i=0; i<commonOptions.length; ++i) {
DocletOption option = commonOptions[i];
registerOption(option);
}
DocletOption[] docletOptions = getOptions();
for (int i=0; i<docletOptions.length; ++i) {
DocletOption option = docletOptions[i];
registerOption(option);
}
optionsRegistered = true;
}
}
protected abstract DocletOption[] getOptions();
private boolean optionsRegistered = false;
private void registerOption(DocletOption option)
{
nameToOptionMap.put(option.getName(), option);
}
private Map nameToOptionMap = new HashMap();
private int getOptionLength(String optionName)
{
registerOptions();
DocletOption option = (DocletOption)nameToOptionMap.get(optionName.toLowerCase());
if (null != option) {
return option.getLength();
}
else {
return -1;
}
}
protected List getKnownDirectSubclasses(ClassDoc classDoc)
{
List result = new LinkedList();
if (!"java.lang.Object".equals(classDoc.qualifiedName())) {
ClassDoc[] classes = rootDoc.classes();
for (int i=0; i<classes.length; ++i) {
if (classDoc == classes[i].superclass()) {
result.add(classes[i]);
}
}
}
return result;
}
protected static class IndexKey
implements Comparable
{
private String name;
private String lowerName;
public IndexKey(String name)
{
this.name = name;
this.lowerName = name.toLowerCase();
}
public boolean equals(Object other)
{
return this.lowerName.equals(((IndexKey)other).lowerName);
}
public int hashCode()
{
return lowerName.hashCode();
}
public int compareTo(Object other)
{
return lowerName.compareTo(((IndexKey)other).lowerName);
}
public String getName()
{
return name;
}
}
private Map categorizedIndex;
protected Map getCategorizedIndex()
{
if (null == categorizedIndex) {
categorizedIndex = new LinkedHashMap();
Map indexMap = getIndexByName();
LinkedList keys = new LinkedList(); //indexMap.keySet().size());
keys.addAll(indexMap.keySet());
Collections.sort(keys);
Iterator it = keys.iterator(); //indexMap.keySet().iterator();
char previousCategoryLetter = '\0';
Character keyLetter = null;
while (it.hasNext()) {
IndexKey key = (IndexKey)it.next();
char firstChar = Character.toUpperCase(key.getName().charAt(0));
if (firstChar != previousCategoryLetter) {
keyLetter = new Character(firstChar);
previousCategoryLetter = firstChar;
categorizedIndex.put(keyLetter, new LinkedList());
}
List letterList = (List)categorizedIndex.get(keyLetter);
letterList.add(indexMap.get(key));
}
}
return categorizedIndex;
}
private Map indexByName;
protected Map getIndexByName()
{
if (null == indexByName) {
// Create index
// Collect index
indexByName = new HashMap(); //TreeMap();
// Add packages to index
PackageDoc[] packages = rootDoc.specifiedPackages();
for (int i=0, ilim=packages.length; i<ilim; ++i) {
PackageDoc c = packages[i];
if (c.name().length() > 0) {
indexByName.put(new IndexKey(c.name()), c);
}
}
// Add classes, fields and methods to index
ClassDoc[] sumclasses = rootDoc.classes();
for (int i=0, ilim=sumclasses.length; i<ilim; ++i) {
ClassDoc c = sumclasses[i];
if (null == c.containingClass()) {
indexByName.put(new IndexKey(c.name() + " " + c.containingPackage().name()), c);
}
else {
indexByName.put(new IndexKey(c.name().substring(c.containingClass().name().length() + 1)
+ " " + c.containingClass().name() + " " + c.containingPackage().name()), c);
}
FieldDoc[] fields = c.fields();
for (int j=0, jlim=fields.length; j<jlim; ++j) {
indexByName.put(new IndexKey(fields[j].name() + " " + fields[j].containingClass().name() + " " + fields[j].containingPackage().name()), fields[j]);
}
MethodDoc[] methods = c.methods();
for (int j=0, jlim=methods.length; j<jlim; ++j) {
MethodDoc method = methods[j];
indexByName.put(new IndexKey(method.name() + method.signature() + " " + method.containingClass().name() + " " + method.containingPackage().name()), method);
}
ConstructorDoc[] constructors = c.constructors();
for (int j=0, jlim=constructors.length; j<jlim; ++j) {
ConstructorDoc constructor = constructors[j];
indexByName.put(new IndexKey(constructor.name() + constructor.signature() + " " + constructor.containingClass().name() + " " + constructor.containingPackage().name()), constructor);
}
}
}
return indexByName;
}
private void registerTaglet(Taglet taglet)
{
tagletMap.put(taglet.getName(), taglet);
}
protected void printTaglets(Tag[] tags, TagletContext context, TagletPrinter output, boolean inline)
{
for (Iterator it = tagletMap.keySet().iterator(); it.hasNext(); ) {
String tagName = (String)it.next();
Object o = tagletMap.get(tagName);
Taglet taglet = (Taglet)o;
Doc doc = context.getDoc();
if (inline == taglet.isInlineTag()
&& ((doc == null
&& taglet.inOverview())
|| (doc != null
&& ((doc.isConstructor() && taglet.inConstructor())
|| (doc.isField() && taglet.inField())
|| (doc.isMethod() && taglet.inMethod())
|| (doc instanceof PackageDoc && taglet.inPackage())
|| ((doc.isClass() || doc.isInterface()) && taglet.inType()))))) {
List tagsOfThisType = new LinkedList();
for (int i=0; i<tags.length; ++i) {
if (tags[i].name().substring(1).equals(tagName)) {
tagsOfThisType.add(tags[i]);
}
}
Tag[] tagletTags = (Tag[])tagsOfThisType.toArray(new Tag[tagsOfThisType.size()]);
String tagletString;
if (taglet instanceof StandardTaglet) {
tagletString = renderTag(tagName, tagletTags, context);
}
else if (taglet instanceof GnuExtendedTaglet) {
tagletString = ((GnuExtendedTaglet)taglet).toString(tagletTags, context);
}
else {
tagletString = taglet.toString(tagletTags);
}
if (null != tagletString) {
output.printTagletString(tagletString);
}
}
}
}
protected void printInlineTaglet(Tag tag, TagletContext context, TagletPrinter output)
{
Taglet taglet = (Taglet)tagletMap.get(tag.name().substring(1));
if (null != taglet) {
String tagletString;
if (taglet instanceof GnuExtendedTaglet) {
tagletString = ((GnuExtendedTaglet)taglet).toString(tag, context);
}
else {
tagletString = taglet.toString(tag);
}
if (null != tagletString) {
output.printTagletString(tagletString);
}
}
else {
printWarning("Unknown tag: " + tag.name());
}
}
protected void printMainTaglets(Tag[] tags, TagletContext context, TagletPrinter output)
{
printTaglets(tags, context, output, false);
}
/**
* @param usedClassToPackagesMap ClassDoc to (PackageDoc to (UsageType to (Set of Doc)))
*/
private void addUsedBy(Map usedClassToPackagesMap,
ClassDoc usedClass, UsageType usageType, Doc user, PackageDoc userPackage)
{
Map packageToUsageTypeMap = (Map)usedClassToPackagesMap.get(usedClass);
if (null == packageToUsageTypeMap) {
packageToUsageTypeMap = new HashMap();
usedClassToPackagesMap.put(usedClass, packageToUsageTypeMap);
}
Map usageTypeToUsersMap = (Map)packageToUsageTypeMap.get(userPackage);
if (null == usageTypeToUsersMap) {
usageTypeToUsersMap = new TreeMap();
packageToUsageTypeMap.put(userPackage, usageTypeToUsersMap);
}
Set userSet = (Set)usageTypeToUsersMap.get(usageType);
if (null == userSet) {
userSet = new TreeSet(); // FIXME: we need the collator from Main here
usageTypeToUsersMap.put(usageType, userSet);
}
userSet.add(user);
}
/**
* Create the cross reference database.
*/
private Map collectUsage() {
Map _usedClassToPackagesMap = new HashMap();
ClassDoc[] classes = rootDoc.classes();
for (int i = 0, ilim = classes.length; i < ilim; ++ i) {
ClassDoc clazz = classes[i];
if (clazz.isInterface()) {
// classes implementing
InterfaceRelation relation
= (InterfaceRelation)getInterfaceRelations().get(clazz);
Iterator it = relation.implementingClasses.iterator();
while (it.hasNext()) {
ClassDoc implementor = (ClassDoc)it.next();
addUsedBy(_usedClassToPackagesMap,
clazz, UsageType.CLASS_IMPLEMENTING, implementor, implementor.containingPackage());
}
}
else {
// classes derived from
for (ClassDoc superclass = clazz.superclass(); superclass != null;
superclass = superclass.superclass()) {
addUsedBy(_usedClassToPackagesMap,
superclass, UsageType.CLASS_DERIVED_FROM, clazz, clazz.containingPackage());
}
}
FieldDoc[] fields = clazz.fields();
for (int j = 0, jlim = fields.length; j < jlim; ++ j) {
FieldDoc field = fields[j];
// fields of type
ClassDoc fieldType = field.type().asClassDoc();
if (null != fieldType) {
addUsedBy(_usedClassToPackagesMap,
fieldType, UsageType.FIELD_OF_TYPE,
field, clazz.containingPackage());
}
}
MethodDoc[] methods = clazz.methods();
for (int j = 0, jlim = methods.length; j < jlim; ++ j) {
MethodDoc method = methods[j];
// methods with return type
ClassDoc returnType = method.returnType().asClassDoc();
if (null != returnType) {
addUsedBy(_usedClassToPackagesMap,
returnType, UsageType.METHOD_WITH_RETURN_TYPE,
method, clazz.containingPackage());
}
Parameter[] parameters = method.parameters();
for (int k=0; k<parameters.length; ++k) {
// methods with parameter type
Parameter parameter = parameters[k];
ClassDoc parameterType = parameter.type().asClassDoc();
if (null != parameterType) {
addUsedBy(_usedClassToPackagesMap,
parameterType, UsageType.METHOD_WITH_PARAMETER_TYPE,
method, clazz.containingPackage());
}
}
// methods which throw
ClassDoc[] thrownExceptions = method.thrownExceptions();
for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) {
ClassDoc thrownException = thrownExceptions[k];
addUsedBy(_usedClassToPackagesMap,
thrownException, UsageType.METHOD_WITH_THROWN_TYPE,
method, clazz.containingPackage());
}
}
ConstructorDoc[] constructors = clazz.constructors();
for (int j = 0, jlim = constructors.length; j < jlim; ++ j) {
ConstructorDoc constructor = constructors[j];
Parameter[] parameters = constructor.parameters();
for (int k = 0, klim = parameters.length; k < klim; ++ k) {
// constructors with parameter type
Parameter parameter = parameters[k];
ClassDoc parameterType = parameter.type().asClassDoc();
if (null != parameterType) {
addUsedBy(_usedClassToPackagesMap,
parameterType, UsageType.CONSTRUCTOR_WITH_PARAMETER_TYPE,
constructor, clazz.containingPackage());
}
}
// constructors which throw
ClassDoc[] thrownExceptions = constructor.thrownExceptions();
for (int k = 0, klim = thrownExceptions.length; k < klim; ++ k) {
ClassDoc thrownException = thrownExceptions[k];
addUsedBy(_usedClassToPackagesMap,
thrownException, UsageType.CONSTRUCTOR_WITH_THROWN_TYPE,
constructor, clazz.containingPackage());
}
}
}
return _usedClassToPackagesMap;
}
private Map usedClassToPackagesMap = null;
protected Map getUsageOfClass(ClassDoc classDoc)
{
if (null == this.usedClassToPackagesMap) {
this.usedClassToPackagesMap = collectUsage();
}
return (Map)this.usedClassToPackagesMap.get(classDoc);
}
protected static class UsageType
implements Comparable
{
public static final UsageType CLASS_DERIVED_FROM = new UsageType("class-derived-from");
public static final UsageType CLASS_IMPLEMENTING = new UsageType("class-implementing");
public static final UsageType FIELD_OF_TYPE = new UsageType("field-of-type");
public static final UsageType METHOD_WITH_RETURN_TYPE = new UsageType("method-with-return-type");
public static final UsageType METHOD_WITH_PARAMETER_TYPE = new UsageType("method-with-parameter-type");
public static final UsageType METHOD_WITH_THROWN_TYPE = new UsageType("method-with-thrown-type");
public static final UsageType CONSTRUCTOR_WITH_PARAMETER_TYPE = new UsageType("constructor-with-parameter-type");
public static final UsageType CONSTRUCTOR_WITH_THROWN_TYPE = new UsageType("constructor-with-thrown-type");
private String id;
private UsageType(String id)
{
this.id = id;
}
public int compareTo(Object other)
{
return this.id.compareTo(((UsageType)other).id);
}
public String toString() {
return "UsageType{id=" + id + "}";
}
public String getId() {
return id;
}
}
private ResourceBundle resources;
protected String getString(String key)
{
if (null == resources) {
Locale currentLocale = Locale.getDefault();
resources
= ResourceBundle.getBundle("htmldoclet.HtmlDoclet", currentLocale);
}
return resources.getString(key);
}
protected String format(String key, String value1)
{
return MessageFormat.format(getString(key), new Object[] { value1 });
}
protected List getPackageGroups()
{
return packageGroups;
}
protected void copyDocFiles(File sourceDir, File targetDir)
throws IOException
{
File sourceDocFiles = new File(sourceDir, "doc-files");
File targetDocFiles = new File(targetDir, "doc-files");
if (sourceDocFiles.exists()) {
IOToolkit.copyDirectory(sourceDocFiles,
targetDocFiles,
optionDocFilesSubDirs.getValue(),
optionExcludeDocFilesSubDir.getComponents());
}
}
private Set sourcePaths;
/**
* Try to determine the source directory for the given package by
* looking at the path specified by -sourcepath, or the current
* directory if -sourcepath hasn't been specified.
*
* @throws IOException if the source directory couldn't be
* located.
*
* @return List of File
*/
protected List getPackageSourceDirs(PackageDoc packageDoc)
throws IOException
{
if (null == sourcePaths) {
for (int i=0; i<rootDoc.options().length; ++i) {
if ("-sourcepath".equals(rootDoc.options()[i][0])
|| "-s".equals(rootDoc.options()[i][0])) {
sourcePaths = new LinkedHashSet();
String sourcepathString = rootDoc.options()[i][1];
StringTokenizer st = new StringTokenizer(sourcepathString, File.pathSeparator);
while (st.hasMoreTokens()) {
sourcePaths.add(new File(st.nextToken()));
}
}
}
if (null == sourcePaths) {
sourcePaths = new LinkedHashSet();
sourcePaths.add(new File(System.getProperty("user.dir")));
}
}
String packageSubDir = packageDoc.name().replace('.', File.separatorChar);
Iterator it = sourcePaths.iterator();
List result = new LinkedList();
while (it.hasNext()) {
File pathComponent = (File)it.next();
File packageDir = new File(pathComponent, packageSubDir);
if (packageDir.exists()) {
result.add(packageDir);
}
}
if (result.isEmpty()) {
throw new IOException("Couldn't locate source directory for package " + packageDoc.name());
}
else {
return result;
}
}
protected File getSourceFile(ClassDoc classDoc)
throws IOException
{
List packageDirs = getPackageSourceDirs(classDoc.containingPackage());
Iterator it = packageDirs.iterator();
while (it.hasNext()) {
File packageDir = (File)it.next();
File sourceFile = new File(packageDir, getOuterClassDoc(classDoc).name() + ".java");
if (sourceFile.exists()) {
return sourceFile;
}
}
throw new IOException("Couldn't locate source file for class " + classDoc.qualifiedTypeName());
}
protected void printError(String error)
{
if (null != rootDoc) {
rootDoc.printError(error);
}
else {
System.err.println("ERROR: "+error);
}
}
protected void printWarning(String warning)
{
if (null != rootDoc) {
rootDoc.printWarning(warning);
}
else {
System.err.println("WARNING: "+warning);
}
}
protected void printNotice(String notice)
{
if (null != rootDoc) {
rootDoc.printNotice(notice);
}
else {
System.err.println(notice);
}
}
protected static ClassDoc getOuterClassDoc(ClassDoc classDoc)
{
while (null != classDoc.containingClass()) {
classDoc = classDoc.containingClass();
}
return classDoc;
}
private SortedSet allPackages;
protected Set getAllPackages()
{
if (null == this.allPackages) {
allPackages = new TreeSet();
PackageDoc[] specifiedPackages = rootDoc.specifiedPackages();
for (int i=0; i<specifiedPackages.length; ++i) {
allPackages.add(specifiedPackages[i]);
}
ClassDoc[] specifiedClasses = rootDoc.specifiedClasses();
for (int i=0; i<specifiedClasses.length; ++i) {
allPackages.add(specifiedClasses[i].containingPackage());
}
}
return this.allPackages;
}
protected boolean omitPackageQualifier(PackageDoc packageDoc)
{
if (!optionNoQualifier.isSpecified()) {
return false;
}
else {
return optionNoQualifier.match(packageDoc);
}
}
protected String possiblyQualifiedName(Type type)
{
if (null == type.asClassDoc()
|| !omitPackageQualifier(type.asClassDoc().containingPackage())) {
return type.qualifiedTypeName();
}
else {
return type.typeName();
}
}
protected static class InterfaceRelation
{
public Set superInterfaces;
public Set subInterfaces;
public Set implementingClasses;
public InterfaceRelation()
{
superInterfaces = new TreeSet();
subInterfaces = new TreeSet();
implementingClasses = new TreeSet();
}
}
private void addAllInterfaces(ClassDoc classDoc, Set allInterfaces)
{
ClassDoc[] interfaces = classDoc.interfaces();
for (int i=0; i<interfaces.length; ++i) {
allInterfaces.add(interfaces[i]);
addAllInterfaces(interfaces[i], allInterfaces);
}
}
private Map allSubClasses;
protected Map getAllSubClasses()
{
if (null == allSubClasses) {
allSubClasses = new HashMap();
ClassDoc[] classDocs = getRootDoc().classes();
for (int i=0; i<classDocs.length; ++i) {
if (!classDocs[i].isInterface()) {
for (ClassDoc cd = classDocs[i].superclass();
null != cd;
cd = cd.superclass()) {
if (!cd.qualifiedTypeName().equals("java.lang.Object")) {
List subClasses = (List)allSubClasses.get(cd);
if (null == subClasses) {
subClasses = new LinkedList();
allSubClasses.put(cd, subClasses);
}
subClasses.add(classDocs[i]);
}
}
}
}
}
return allSubClasses;
}
private Map interfaceRelations;
private void addToInterfaces(ClassDoc classDoc, ClassDoc[] interfaces)
{
for (int i=0; i<interfaces.length; ++i) {
InterfaceRelation interfaceRelation
= (InterfaceRelation)interfaceRelations.get(interfaces[i]);
if (null == interfaceRelation) {
interfaceRelation = new InterfaceRelation();
interfaceRelations.put(interfaces[i], interfaceRelation);
}
interfaceRelation.implementingClasses.add(classDoc);
addToInterfaces(classDoc, interfaces[i].interfaces());
}
}
protected Map getInterfaceRelations()
{
if (null == interfaceRelations) {
interfaceRelations = new HashMap();
ClassDoc[] classDocs = getRootDoc().classes();
for (int i=0; i<classDocs.length; ++i) {
if (classDocs[i].isInterface()) {
InterfaceRelation relation = new InterfaceRelation();
addAllInterfaces(classDocs[i], relation.superInterfaces);
interfaceRelations.put(classDocs[i], relation);
}
}
Iterator it = interfaceRelations.keySet().iterator();
while (it.hasNext()) {
ClassDoc interfaceDoc = (ClassDoc)it.next();
InterfaceRelation relation
= (InterfaceRelation)interfaceRelations.get(interfaceDoc);
Iterator superIt = relation.superInterfaces.iterator();
while (superIt.hasNext()) {
ClassDoc superInterfaceDoc = (ClassDoc)superIt.next();
InterfaceRelation superRelation
= (InterfaceRelation)interfaceRelations.get(superInterfaceDoc);
if (null != superRelation) {
superRelation.subInterfaces.add(interfaceDoc);
}
}
}
for (int i=0; i<classDocs.length; ++i) {
if (!classDocs[i].isInterface()) {
for (ClassDoc cd = classDocs[i]; null != cd; cd = cd.superclass()) {
addToInterfaces(classDocs[i], cd.interfaces());
}
}
}
}
return interfaceRelations;
}
private Map sortedMethodMap = new HashMap();
protected MethodDoc[] getSortedMethods(ClassDoc classDoc)
{
MethodDoc[] result = (MethodDoc[])sortedMethodMap.get(classDoc);
if (null == result) {
MethodDoc[] methods = classDoc.methods();
result = (MethodDoc[])methods.clone();
Arrays.sort(result);
return result;
}
return result;
}
private Map sortedConstructorMap = new HashMap();
protected ConstructorDoc[] getSortedConstructors(ClassDoc classDoc)
{
ConstructorDoc[] result = (ConstructorDoc[])sortedConstructorMap.get(classDoc);
if (null == result) {
ConstructorDoc[] constructors = classDoc.constructors();
result = (ConstructorDoc[])constructors.clone();
Arrays.sort(result);
return result;
}
return result;
}
private Map sortedFieldMap = new HashMap();
protected FieldDoc[] getSortedFields(ClassDoc classDoc)
{
FieldDoc[] result = (FieldDoc[])sortedFieldMap.get(classDoc);
if (null == result) {
FieldDoc[] fields = classDoc.fields();
result = (FieldDoc[])fields.clone();
Arrays.sort(result);
return result;
}
return result;
}
private Map sortedInnerClassMap = new HashMap();
protected ClassDoc[] getSortedInnerClasses(ClassDoc classDoc)
{
ClassDoc[] result = (ClassDoc[])sortedInnerClassMap.get(classDoc);
if (null == result) {
ClassDoc[] innerClasses = classDoc.innerClasses();
result = (ClassDoc[])innerClasses.clone();
Arrays.sort(result);
return result;
}
return result;
}
protected abstract String renderTag(String tagName, Tag[] tags, TagletContext context);
protected abstract String getDocletVersion();
protected SortedSet getThrownExceptions(ExecutableMemberDoc execMemberDoc)
{
SortedSet result = new TreeSet();
ClassDoc[] thrownExceptions = execMemberDoc.thrownExceptions();
for (int j=0; j<thrownExceptions.length; ++j) {
result.add(thrownExceptions[j]);
}
return result;
}
protected boolean isUncheckedException(ClassDoc classDoc)
{
if (classDoc.isException()) {
while (null != classDoc) {
if (classDoc.qualifiedTypeName().equals("java.lang.RuntimeException")) {
return true;
}
classDoc = classDoc.superclass();
}
return false;
}
else {
return false;
}
}
protected FieldDoc findField(ClassDoc classDoc, String fieldName)
{
for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) {
FieldDoc[] fields = cd.fields(false);
for (int i=0; i<fields.length; ++i) {
if (fields[i].name().equals(fieldName)) {
return fields[i];
}
}
}
return null;
}
private Map implementedInterfacesCache = new HashMap();
protected Set getImplementedInterfaces(ClassDoc classDoc)
{
Set result = (Set)implementedInterfacesCache.get(classDoc);
if (null == result) {
result = new TreeSet();
for (ClassDoc cd = classDoc; cd != null; cd = cd.superclass()) {
ClassDoc[] interfaces = cd.interfaces();
for (int i=0; i<interfaces.length; ++i) {
result.add(interfaces[i]);
InterfaceRelation relation
= (InterfaceRelation)getInterfaceRelations().get(interfaces[i]);
if (null != relation) {
result.addAll(relation.superInterfaces);
}
}
}
implementedInterfacesCache.put(classDoc, result);
}
return result;
}
protected boolean isSinglePackage()
{
return getAllPackages().size() <= 1;
}
protected PackageDoc getSinglePackage()
{
return (PackageDoc)getAllPackages().iterator().next();
}
}